﻿using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Linq;
using System.Text.RegularExpressions;
using System.Text;

namespace SharePoint.Accelerators.Lists.SearchSuggest.SearchSuggestWebPart
{
    /// <summary>
    /// Source: SPDevWiki: http://tinyurl.com/ygrzvbc
    /// </summary>
    public static class Extensions
    {
        public static bool ListExists(this SPWeb web, string listName)
        {
            return web.Lists.Cast<SPList>().Any(list => string.Compare(list.Title, listName, true) == 0);
        }
    }

    [ToolboxItemAttribute(false)]
    public class SearchSuggestWebPart : WebPart
    {
        public SearchSuggestWebPart()
        {
        }

        private string _jQueryPath = "{0}/JQuery/jquery-1.7.1.min.js";
        private string _jQueryUIPath = "{0}/JQuery/jquery-ui-1.8.17.custom.min.js";
        private string _searchSuggestPath = "{0}/JQuery/SearchSuggest.js";
        private string _cssFile = "{0}/JQuery/CSS/ui-lightness/jquery-ui-1.8.17.custom.css";
        private string _listName = "";
        //private string _searchFields = "Fullname";
        private string _displayFields = "Result: [Title]";
        private string _queryFields = "Title";
        private const string _basicDisplayFields = "DisplayName, ContentType, File, Id, Created, Modified";
        private string _searchLabel = "Search for: ";
        private string _textBoxWidth = "300px";

        [WebBrowsable(true)]
        [WebDisplayName("Search TextBox Width")]
        [WebDescription("Search TextBox Width.")]
        [Personalizable(PersonalizationScope.Shared)]
        public string TextBoxWidth
        {
            get { return _textBoxWidth; }
            set { _textBoxWidth = value; }
        }

        [WebBrowsable(true)]
        [WebDisplayName("Search Label")]
        [WebDescription("Search Label.")]
        [Personalizable(PersonalizationScope.Shared)]
        public string SearchLabel
        {
            get { return _searchLabel; }
            set { _searchLabel = value; }
        }

        [WebBrowsable(true)]
        [WebDisplayName("List Name")]
        [WebDescription("Name of the list that will be searched")]
        [Personalizable(PersonalizationScope.Shared)]
        [Category("Search")]
        public string ListName
        {
            get { return _listName; }
            set
            {
                SPWeb web = SPContext.Current.Web;
                if (web.ListExists(value))
                {
                    _listName = value;
                }
                else
                {
                    _listName = "";
                }
            }
        }

        [WebBrowsable(true)]
        [WebDisplayName("Display Fields Format")]
        [WebDescription("Format output search results. Each field should be in square brackets.")]
        [Personalizable(PersonalizationScope.Shared)]
        [Category("Search")]
        public string DisplayFieldsFormat
        {
            get { return _displayFields; }
            set
            {
                _displayFields = value;
            }
        }

        [WebBrowsable(true)]
        [WebDisplayName("Search Fields")]
        [WebDescription("The list of fields that will be queried. Seperate multiple fields with semicolon.")]
        [Personalizable(PersonalizationScope.Shared)]
        [Category("Search")]
        public string QueryFields
        {
            get { return _queryFields; }
            set
            {
                _queryFields = value;
            }
        }

        [WebBrowsable(true)]
        [WebDisplayName("CSS File Path")]
        [WebDescription("Path to CSS File.")]
        [Personalizable(PersonalizationScope.Shared)]
        [Category("Resources")]
        public string CssFile
        {
            get { return _cssFile; }
            set { _cssFile = value; }
        }

        [WebBrowsable(true)]
        [WebDisplayName("Search Suggest Path")]
        [WebDescription("Path to Search Sugest Library.")]
        [Personalizable(PersonalizationScope.Shared)]
        [Category("Resources")]
        public string SearchSuggestPath
        {
            get { return _searchSuggestPath; }
            set { _searchSuggestPath = value; }
        }

        [WebBrowsable(true)]
        [WebDisplayName("JQuery UI Path")]
        [WebDescription("Path to JQuery UI Library.")]
        [Personalizable(PersonalizationScope.Shared)]
        [Category("Resources")]
        public string JQueryUIPath
        {
            get { return _jQueryUIPath; }
            set { _jQueryUIPath = value; }
        }

        [WebBrowsable(true)]
        [WebDisplayName("JQuery Path")]
        [WebDescription("Path to JQuery Library.")]
        [Personalizable(PersonalizationScope.Shared)]
        [Category("Resources")]
        public string JQueryPath
        {
            get { return _jQueryPath; }
            set { _jQueryPath = value; }
        }

        protected override void CreateChildControls()
        {
            try
            {
                string webUrl = SPContext.Current.Web.ServerRelativeUrl;

                if (webUrl == "/")
                {
                    webUrl = "";
                }

                LiteralControl listNameBlock = new LiteralControl();
                listNameBlock.Text = string.Format("<script type=\"text/javascript\">var targetList = '{0}';</script>", ListName);

                Controls.Add(listNameBlock);

                HtmlGenericControl jQueryLink = new HtmlGenericControl("script");
                jQueryLink.Attributes.Add("type", "text/javascript");
                jQueryLink.Attributes.Add("src", string.Format(JQueryPath, webUrl));
                Controls.Add(jQueryLink);

                HtmlGenericControl jQueryUILink = new HtmlGenericControl("script");
                jQueryUILink.Attributes.Add("type", "text/javascript");
                jQueryUILink.Attributes.Add("src", string.Format(JQueryUIPath, webUrl));
                Controls.Add(jQueryUILink);

                HtmlGenericControl csslink = new HtmlGenericControl("LINK");
                csslink.Attributes.Add("rel", "stylesheet");
                csslink.Attributes.Add("type", "text/css");
                csslink.Attributes.Add("href", string.Format(CssFile, webUrl));
                Controls.Add(csslink);

                string disabled = "disabled='disabled'";
                string defaultDisplayFormUrl = "";
                string warningText = "";

                SPWeb web = SPContext.Current.Web;
                if (web.ListExists(ListName))
                {
                    defaultDisplayFormUrl = web.Lists[ListName].DefaultDisplayFormUrl;
                    disabled = "";
                }
                else
                {
                    warningText = "<div style=\"color:red\">Please enter a list name (via Edit Web Part pane)!</div>";
                }


             
                LiteralControl literal = new LiteralControl();
                literal.Text = string.Format("<div class=\"demo\"><div class=\"ui-widget\"><label for=\"{3}\">{0}</label><input title=\"Search...\" style=\"width:{1}\" id=\"{3}\" {2}></div></div>", SearchLabel, TextBoxWidth, disabled, this.ID);
                literal.Text += "<style>input.exampleText {color: #aaa;}</style>";

                literal.Text += "<script type=\"text/javascript\">";
                literal.Text += string.Format("var defaultDisplayFormUrl = '{0}';", defaultDisplayFormUrl);
                literal.Text += string.Format("var displayFields = '{0}, {1}';", _basicDisplayFields, GetFieldsList(GetFormatFields(DisplayFieldsFormat)));
                literal.Text += string.Format("var camlQuery = '{0}';", GetCAMLQuery(GetQueryFields(QueryFields), this.ListName));
                literal.Text += string.Format("function GetFieldValues(fieldValues) {{ return {0}; }}", GenerateArgumentList(DisplayFieldsFormat));
                literal.Text += @"function Initialize() {
                                    $('#" + this.ID + @"').autocomplete({
                                        focus: function (event, ui) {
                                            $('#" + this.ID + @"').val(ui.item.label);
                                            return false;
                                        },
                                        select: function (event, ui) {
                                            window.location = defaultDisplayFormUrl + '?ID=' + ui.item.desc;
                                        },
                                        source: function (request, response) {
                                            queryItems(request.term, response);
                                        }
                                    });
                                }

                                function switchText() {
                                    if ($(this).val() == $(this).attr('title'))
                                        $(this).val('').removeClass('exampleText');
                                    else if ($.trim($(this).val()) == '')
                                        $(this).addClass('exampleText').val($(this).attr('title'));
                                }

                                $('#" + this.ID + @"').each(function () {
                                    if ($.trim($(this).val()) == '') $(this).val($(this).attr('title'));
                                    if ($(this).val() == $(this).attr('title')) $(this).addClass('exampleText');
                                }).focus(switchText).blur(switchText);";

                literal.Text += "</script>";

                literal.Text += warningText;
                Controls.Add(literal);

                HtmlGenericControl searchSuggestLink = new HtmlGenericControl("script");
                searchSuggestLink.Attributes.Add("type", "text/javascript");
                searchSuggestLink.Attributes.Add("src", string.Format(SearchSuggestPath, webUrl));
                Controls.Add(searchSuggestLink);
                


            }
            catch (Exception ex)
            {
                AddSharePointNotification(ex.Message);
            }
            finally
            {
                base.CreateChildControls();
            }
        }

        protected override void RenderContents(HtmlTextWriter writer)
        {
            base.RenderContents(writer);
        }

        private string[] GetFormatFields(string FieldList)
        {
            var pattern = @"\[(.*?)\]";
            var matches = Regex.Matches(FieldList, pattern);

            string stringFormat = FieldList;
            string[] fieldList = new string[matches.Count];
            int i = 0;

            foreach (Match m in matches)
            {
                fieldList[i++] = m.Groups[1].ToString();
            }

            return fieldList;
        }

        private string[] GetQueryFields(string FieldList)
        {
            string[] fields = FieldList.Split(';');

            for (int i = 0; i < fields.Length; i++)
            {
                fields[i] = fields[i].Trim();
            }

            return fields;
        }

        /// <summary>
        /// Add SharePoint UI Notification from C#, source: http://tinyurl.com/6mz3tgu
        /// </summary>
        /// <param name="text"></param>
        public void AddSharePointNotification(string text)
        {
            //build up javascript to inject at the tail end of the page 
            StringBuilder stringBuilder = new StringBuilder();

            stringBuilder.AppendLine("<script>");

            //First wait until the SP.js is loaded, otherwise the notification doesn’t work 
            //gets an null reference exception 
            stringBuilder.AppendLine("ExecuteOrDelayUntilScriptLoaded(ShowNotification, \"sp.js\");");

            stringBuilder.AppendLine("function ShowNotification()");
            stringBuilder.AppendLine("{");
            stringBuilder.AppendLine(string.Format("SP.UI.Status.addStatus(\"Search AutoComplete for Lists\", \"{0}\", true);", text));
            stringBuilder.AppendLine("}");

            stringBuilder.AppendLine("</script>");

            //add to the page 
            Controls.Add(new LiteralControl(stringBuilder.ToString()));

        }

        //public void AddSharePointNotification(string message)
        //{
        //    StringBuilder stringBuilder = new StringBuilder();

        //    stringBuilder.AppendLine("<script>");

        //    stringBuilder.AppendLine("ExecuteOrDelayUntilScriptLoaded(ShowException, \"sp.js\");");
        //    stringBuilder.AppendLine("function ShowException(title, message) {var statusId = SP.UI.Status.addStatus(title, message, true);SP.UI.Status.setStatusPriColor(statusId, 'red');}");

        //    stringBuilder.AppendLine("ShowException('Exception Occured','" + message + "');");
            
        //    stringBuilder.AppendLine("</script>");

        //    Controls.Add(new LiteralControl(stringBuilder.ToString()));
        //}

        private string GetCAMLQuery(string[] FieldsList, string ListName)
        {
            try
            {
                SPWeb currentWeb = SPContext.Current.Web;
                SPList list = SPContext.Current.Web.Lists[ListName];

                string query = "";
                string fieldQuery;

                foreach (string field in FieldsList)
                {
                    string type = list.Fields.GetFieldByInternalName(field).TypeAsString;
                    if(type == "BusinessData")
                    {
                        type = "Text";
                    }


                    fieldQuery = string.Format("<Contains><FieldRef Name=\"{0}\" /><Value Type=\"{1}\">{{0}}</Value></Contains>", field, type);

                    if (query.Length == 0)
                    {
                        query = fieldQuery;
                    }
                    else
                    {
                        query = string.Format("<Or>{0}{1}</Or>", query, fieldQuery);
                    }
                }

                // excluding folders
                return string.Format("<And>{0}{1}</And>", query, "<Neq><FieldRef Name=\"ContentType\" /><Value Type=\"Computed\">Folder</Value></Neq>");
            }
            catch(Exception ex)
            {
                AddSharePointNotification(ex.Message.Replace("'", ""));
                return "<Contains><FieldRef Name=\"Title\" /><Value Type=\"Text\">{0}</Value></Contains>";
            }

        }

        private string GetFieldsList(string[] FieldsList)
        {
            StringBuilder fieldsList = new StringBuilder();

            foreach (string field in FieldsList)
            {
                if (fieldsList.Length == 0)
                {
                    fieldsList.Append(field);
                }
                else
                {
                    fieldsList.Append(string.Format(", {0}", field));
                }
            }

            return fieldsList.ToString();
        }

        private string GenerateArgumentList(string FieldsList)
        {
            string[] fieldsList = GetFormatFields(FieldsList);
            string stringFormat = FieldsList;

            StringBuilder argumentList = new StringBuilder();
            int i = 0;

            foreach (string field in fieldsList)
            {
                stringFormat = stringFormat.Replace("[" + field + "]", "{" + i++ + "}");

                if (argumentList.Length == 0)
                {
                    argumentList.Append(string.Format("fieldValues[\"{0}\"]", field));
                }
                else
                {
                    argumentList.Append(string.Format(", fieldValues[\"{0}\"]", field));
                }
            }

            return string.Format("String.format(\"{0}\", {1})", stringFormat, argumentList.ToString());
        }
    }
}
